//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.TridentWordAddIn.Common;
using Microsoft.TridentWordAddIn.ResourceHelper;
using Microsoft.TridentWordAddIn.Server;

namespace Microsoft.TridentWordAddIn.Presentation
{
    /// <summary>
    /// 
    /// </summary>
    public partial class SelectWorkflowToRerunUserControl : BaseUserControl, IDisposable
    {
        private bool doNotUpdateUI = false;
        private ICollection<WorkflowAssociation> workflowAssociationList;

        /// <summary>
        /// Initializes a new instance of the <see cref="SelectWorkflowToRerunUserControl"/> class.
        /// </summary>
        public SelectWorkflowToRerunUserControl()
        {
            InitializeComponent();
            this.workflowAssociationList = new List<WorkflowAssociation>();
        }

        private delegate WorkflowAssociation ReturnValueDelegate();

        /// <summary>
        /// Gets the selected association.
        /// </summary>
        /// <value>The selected association.</value>
        public WorkflowAssociation SelectedAssociation
        {
            get
            {
                if (this.InvokeRequired)
                {
                    IAsyncResult result = this.BeginInvoke(new ReturnValueDelegate(this.GetAssociation));
                    result.AsyncWaitHandle.WaitOne();
                    return this.EndInvoke(result) as WorkflowAssociation;
                }
                else
                {
                    return this.GetAssociation();
                }
            }
        }

        /// <summary>
        /// Initializes the specified connection auto update.
        /// </summary>
        /// <param name="connectionAutoUpdate">if set to <c>true</c> [connection auto update].</param>
        public override void Initialize(bool connectionAutoUpdate)
        {
            base.Initialize(connectionAutoUpdate);
            this.ControllerInstance.OnPictureSelectionChanged += new EventHandler<PictureSelectionChangedEventArgs>(Controller_OnPictureSelectionChanged);
            this.ControllerInstance.OnAssociationChanged += new EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
            this.ControllerInstance.OnRerunClicked += new EventHandler<SelectedAssociationEventArgs>(Controller_OnRerunClicked);
            this.PopulateWorkflows(this.ControllerInstance.SelectedPicture);
        }

        #region Event Handlers

        /// <summary>
        /// Occurs when [on selected workflow association changed].
        /// </summary>
        public event EventHandler<SelectedJobChangedEventArgs> OnSelectedAssociationChanged;

        /// <summary>
        /// Occurs when [on association list updated].
        /// </summary>
        public event EventHandler<JobsUpdatedEventArgs> OnAssociationListUpdated;

        #endregion
    
        #region Private Methods

        /// <summary>
        /// Populates the workflows.
        /// </summary>
        private void PopulateWorkflows(DocumentPicture picture)
        {
            this.lvAssociation.Items.Clear();
            this.workflowAssociationList = ControllerInstance.GetAllWorkflowAssociations(DocumentKey, picture);

            this.DisplayResult();
        }

        /// <summary>
        /// Displays the result.
        /// </summary>
        private void DisplayResult()
        {
            WorkflowAssociation association = this.SelectedAssociation;
            this.lvAssociation.Items.Clear();

            if (this.workflowAssociationList.Count == 0)
            {
                ListViewItem noWorkflowsFound = this.lvAssociation.Items.Add(string.Empty);
                noWorkflowsFound.SubItems.Add("No workflow associations present.");
                this.AssociationListUpdation(0);
            }
            else
            {
                Model model = ControllerInstance.GetModelByDocumentKey(DocumentKey);
                foreach (WorkflowAssociation workflowAssociation in this.workflowAssociationList)
                {
                    if (model.Workflows.ContainsKey(workflowAssociation.WorkflowId))
                    {
                        Workflow workflow = model.Workflows[workflowAssociation.WorkflowId];
                        ListViewItem newItem = this.lvAssociation.Items.Add(workflowAssociation.Name);
                        newItem.ImageKey = workflow.Linked ? "LinkedAssociation" : "Association";
                        newItem.SubItems.Add(workflowAssociation.Job.Name);
                        newItem.SubItems.Add(workflowAssociation.Job.DisplayId);
                        newItem.Tag = workflowAssociation;
                    }
                }
            }

            this.AssociationListUpdation(lvAssociation.Items.Count);
            this.OnUpdatedSelecteAssociation(association);
        }

        /// <summary>
        /// Called when [updated selecte association].
        /// </summary>
        /// <param name="association">The association.</param>
        private void OnUpdatedSelecteAssociation(WorkflowAssociation association)
        {
            int index = 0;
            bool selected = false;
            string associationName = association == null ? string.Empty : association.Name;
            string selectedAssociation = string.Empty;
            doNotUpdateUI = true;

            foreach (WorkflowAssociation workflowAssociation in this.workflowAssociationList)
            {
                if (workflowAssociation.Name.Compare(associationName) || associationName.IsNullOrEmpty())
                {
                    selectedAssociation = workflowAssociation.Name;
                    this.lvAssociation.Items[index].Selected = true;
                    selected = true;
                    break;
                }

                index++;
            }

            if (selected == false && this.lvAssociation.Items.Count > 0)
            {
                this.lvAssociation.Items[0].Selected = true;
                foreach (WorkflowAssociation workflowAssociation in this.workflowAssociationList)
                {
                    selectedAssociation = workflowAssociation.Name;
                    break;
                }
            }

            doNotUpdateUI = false;
            if (!associationName.Compare(selectedAssociation))
            {
                this.AssociationSelectionChanged();
            }
        }

        /// <summary>
        /// Called when [job list updation].
        /// </summary>
        private void AssociationListUpdation(int count)
        {
            if (this.OnAssociationListUpdated != null)
            {
                JobsUpdatedEventArgs eventErgs = new JobsUpdatedEventArgs();
                eventErgs.Count = count;
                this.OnAssociationListUpdated(this, eventErgs);
            }
        }

        /// <summary>
        /// Associations the selection changed.
        /// </summary>
        private void AssociationSelectionChanged()
        {
            if (this.OnSelectedAssociationChanged != null)
            {
                Workflow workflow = null;
                WorkflowJob job = null;
                if (this.lvAssociation.SelectedIndices.Count > 0)
                {
                    WorkflowAssociation association = this.GetAssociation();
                    if (association != null)
                    {
                        workflow = this.ControllerInstance.WorkflowById(DocumentKey, association.WorkflowId);
                        job = association.Job;
                    }
                }

                this.OnSelectedAssociationChanged(this, new SelectedJobChangedEventArgs(job, workflow));
            }
        }

        /// <summary>
        /// Gets the association.
        /// </summary>
        /// <returns></returns>
        private WorkflowAssociation GetAssociation()
        {
            if (this.lvAssociation.SelectedIndices.Count > 0)
            {
                return this.lvAssociation.SelectedItems[0].Tag as WorkflowAssociation;
            }

            return null;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the SelectedIndexChanged event of the lvAssociation control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lvAssociation_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.lvAssociation.SelectedIndices.Count > 0 && !doNotUpdateUI)
            {
                this.AssociationSelectionChanged();
            }
        }

        /// <summary>
        /// Handles the MouseUp event of the lvAssociation control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void lvAssociation_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.lvAssociation.SelectedIndices.Count == 0)
            {
                this.OnSelectedAssociationChanged(this, new SelectedJobChangedEventArgs(null, null));
            }
        }

        /// <summary>
        /// Handles the OnPictureSelectionChanged event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.PictureSelectionChangedEventArgs"/> instance containing the event data.</param>
        private void Controller_OnPictureSelectionChanged(object sender, PictureSelectionChangedEventArgs e)
        {
            if (e.DocumentKey == this.DocumentKey)
            {
                this.PopulateWorkflows(e.Picture);
            }
        }

        /// <summary>
        /// Handles the OnAssociationChanged event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.AssociationChangedEventArgs"/> instance containing the event data.</param>
        private void Controller_OnAssociationChanged(object sender, AssociationChangedEventArgs e)
        {
            if (e.DocumentKey == this.DocumentKey)
            {
                this.workflowAssociationList = new List<WorkflowAssociation>();
                DocumentPicture picture = ControllerInstance.SelectedPicture;
                for (int i = 0; i < e.Associations.Count; i++)
                {
                    if (e.Associations[i].Picture.Key.Compare(picture.Key))
                    {
                        this.workflowAssociationList.Add(e.Associations[i]);
                    }
                }

                this.DisplayResult();
            }
        }

        /// <summary>
        /// Handles the OnRerunClicked event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.SelectedAssociationEventArgs"/> instance containing the event data.</param>
        private void Controller_OnRerunClicked(object sender, SelectedAssociationEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                int selectedIndex = -1;
                foreach (WorkflowAssociation association in this.workflowAssociationList)
                {
                    selectedIndex++;
                    if (association.LocalId == e.Association.LocalId)
                    {
                        this.lvAssociation.Items[selectedIndex].Selected = true;
                        this.lvAssociation.Select();
                        break;
                    }
                }
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"/>.
        /// </summary>
        void IDisposable.Dispose()
        {
            this.Disposing(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the specified the privately held resources.
        /// </summary>
        /// <param name="disposing">if set to <c>true</c> disposes resources.</param>
        private void Disposing(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    this.ControllerInstance.OnPictureSelectionChanged -= new EventHandler<PictureSelectionChangedEventArgs>(Controller_OnPictureSelectionChanged);
                    this.ControllerInstance.OnAssociationChanged -= new EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
                    this.ControllerInstance.OnRerunClicked -= new EventHandler<SelectedAssociationEventArgs>(Controller_OnRerunClicked);
                    base.Disposing();
                }
            }

            this.disposed = true;
        }

        #endregion
    }
}
